package net.qiqbframework.domains.event.infra.persistence;

import net.qiqbframework.domains.DomainEvent;
import net.qiqbframework.domains.event.domain.types.Feedback;
import net.qiqbframework.domains.event.domain.types.FeedbackStatus;
import net.qiqbframework.domains.event.domain.types.InstanceSubscription;
import net.qiqbframework.eventhandling.DomainEventMessage;
import net.qiqbframework.eventhandling.EventMessage;
import net.qiqbframework.eventsourcing.dao.*;
import net.qiqbframework.persisthanding.dao.BatchBasicEntityPoDiff;
import net.qiqbframework.serialization.SerializedObject;
import net.qiqbframework.serialization.Serializer;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;

public class DomainEventPersistence {

    private final DomainEventDao domainEventDao;

    private final DomainEventSubscriptionDao domainEventSubscriptionDao;

    private final DomainEventFeedbackDao domainEventFeedbackDao;

    public DomainEventPersistence(DomainEventDao domainEventDao, DomainEventSubscriptionDao domainEventSubscriptionDao, DomainEventFeedbackDao domainEventFeedbackDao) {
        this.domainEventDao = domainEventDao;
        this.domainEventSubscriptionDao = domainEventSubscriptionDao;
        this.domainEventFeedbackDao = domainEventFeedbackDao;
    }

    public void add(List<? extends EventMessage<?>> newDomainEvent, Serializer serializer) {
        domainEventDao.batchInsertPos(newDomainEvent.stream().map(e -> formatPO(e, serializer)).toList());
    }


    public void modify(DomainEvent newDomainEvent, DomainEvent oldDomainEvent, Serializer serializer) {
        // domainEventDao.dynamicUpdateById(formatPO(newDomainEvent, serializer), formatPO(oldDomainEvent, serializer));
        BatchBasicEntityPoDiff.dynamicUpdate(formatNoticeList(newDomainEvent), formatNoticeList(oldDomainEvent), domainEventSubscriptionDao);
        BatchBasicEntityPoDiff.dynamicUpdate(formatFeedbackList(newDomainEvent), formatFeedbackList(oldDomainEvent), domainEventFeedbackDao);
    }


    public DomainEvent loadById(String id) {
        final DomainEventPo domainEventPo = domainEventDao.getPoById(id);
        if (domainEventPo == null) {
            return null;
        }
        final List<DomainEventSubscriptionPo> domainEventSubscriptionPos = domainEventSubscriptionDao.listByEventId(id);
        final List<DomainEventFeedbackPo> domainEventFeedbackPos = domainEventFeedbackDao.listByEventId(id);

        final List<InstanceSubscription> subscriptions = getDomainEventSubscriptions(domainEventSubscriptionPos);
        final List<Feedback> feedbacks = getDomainEventFeedback(domainEventFeedbackPos, domainEventPo.getCreateTime());
        DomainEvent domainEvent = new DomainEvent();
        domainEvent.setId(domainEventPo.getId());
        domainEvent.setFeedbacks(feedbacks);
        domainEvent.setSubscriptions(subscriptions);

        return domainEvent;
    }

    private List<Feedback> getDomainEventFeedback(List<DomainEventFeedbackPo> domainEventFeedbackPos, LocalDateTime eventCreateTime) {
        final List<Feedback> result = new ArrayList<>();
        if (domainEventFeedbackPos != null) {
            for (DomainEventFeedbackPo feedbackPo : domainEventFeedbackPos) {
                final Feedback e = new Feedback(feedbackPo.getId(), FeedbackStatus.valueOf(feedbackPo.getStatus()), eventCreateTime);
                e.setInstanceName(feedbackPo.getInstanceName());
                e.setApplicationName(feedbackPo.getApplicationName());
                e.setProcessListenerId(feedbackPo.getProcessListenerId());
                e.setProcessId(feedbackPo.getProcessId());
                e.setProcessBeginTime(feedbackPo.getProcessBeginTime());
                e.setProcessEndTime(feedbackPo.getProcessEndTime());
                e.setProcessResultMsg(feedbackPo.getProcessResultMsg());
                e.setFeedbackTime(feedbackPo.getFeedbackTime());
                result.add(e);
            }
        }
        return result;
    }

    private List<InstanceSubscription> getDomainEventSubscriptions(List<DomainEventSubscriptionPo> domainEventSubscriptionPos) {
        final List<InstanceSubscription> subscriptions = new ArrayList<>();
        if (domainEventSubscriptionPos != null) {
            for (DomainEventSubscriptionPo domainEventSubscriptionPo : domainEventSubscriptionPos) {
                final InstanceSubscription e = new InstanceSubscription(domainEventSubscriptionPo.getId(), domainEventSubscriptionPo.getApplicationName(), domainEventSubscriptionPo.getInstanceName());
                e.setProcessListenerQty(domainEventSubscriptionPo.getProcessListenerQty());

                subscriptions.add(e);
            }
        }
        return subscriptions;
    }


    private List<DomainEventFeedbackPo> formatFeedbackList(DomainEvent domainEvent) {
        List<DomainEventFeedbackPo> result = new ArrayList<>();
        if (domainEvent.getFeedbacks() != null) {
            for (Feedback feedback : domainEvent.getFeedbacks()) {
                final DomainEventFeedbackPo feedbackPo = new DomainEventFeedbackPo();
                feedbackPo.setId(feedback.getId());
                feedbackPo.setEventId(domainEvent.getId());
                feedbackPo.setStatus(feedback.getStatus().name());
                feedbackPo.setApplicationName(feedback.getApplicationName());
                feedbackPo.setInstanceName(feedback.getInstanceName());
                // 处理信息
                feedbackPo.setProcessListenerId(feedback.getProcessListenerId());
                feedbackPo.setProcessId(feedback.getProcessId());
                feedbackPo.setProcessDuration(feedback.getProcessDuration());
                feedbackPo.setSubscriptDuration(feedback.getSubscriptDuration());
                feedbackPo.setFeedbackTime(feedback.getFeedbackTime());
                feedbackPo.setProcessBeginTime(feedback.getProcessBeginTime());
                feedbackPo.setProcessEndTime(feedback.getProcessEndTime());
                feedbackPo.setProcessResultMsg(feedback.getProcessResultMsg());
                result.add(feedbackPo);
            }
        }
        return result;
    }

    private List<DomainEventSubscriptionPo> formatNoticeList(DomainEvent domainEvent) {
        List<DomainEventSubscriptionPo> result = new ArrayList<>();
        if (domainEvent.getSubscriptions() != null) {
            for (InstanceSubscription subscription : domainEvent.getSubscriptions()) {
                final DomainEventSubscriptionPo subscriptionPo = new DomainEventSubscriptionPo();
                subscriptionPo.setId(subscription.getId());
                subscriptionPo.setEventId(domainEvent.getId());
                subscriptionPo.setProcessListenerQty(subscription.getProcessListenerQty());
                subscriptionPo.setProcessFeedbackListenerQty(domainEvent.getFeedbackQty(subscription.getId()));
                subscriptionPo.setApplicationName(subscription.getApplicationName());
                subscriptionPo.setInstanceName(subscription.getInstanceName());
                result.add(subscriptionPo);
            }
        }


        return result;
    }

    private DomainEventPo formatPO(EventMessage<?> event, Serializer serializer) {
        DomainEventPo po = new DomainEventPo();
        po.setId(event.getIdentifier());
        // 创建时间
        final LocalDateTime localDateTime = LocalDateTime.ofInstant(event.getTimestamp(), ZoneId.systemDefault());

        po.setCreateTime(localDateTime);
        po.setTimestamp(event.getTimestamp().toEpochMilli());
        // 主体
        final SerializedObject<byte[]> serializedObject = event.serializePayload(serializer, byte[].class);
        po.setPayload(serializedObject.getData());
        // 事件主体类名称
        po.setPayloadType(event.getPayloadType().getName());
        final SerializedObject<String> metaDataSerializedObject = event.serializeMetaData(serializer, String.class);
        po.setMetaData(metaDataSerializedObject.getData());
        // 事件主体参数
        // 事件源
        if (event instanceof DomainEventMessage<?> domainEventMessage) {
            po.setSource(domainEventMessage.getAggregateType() + "$$" + domainEventMessage.getAggregateIdentifier());
            po.setAggregateIdentifier(domainEventMessage.getAggregateIdentifier());

            po.setAggregateName(domainEventMessage.getAggregateType());
        }
        return po;
    }
}
